/*
 * RSA.cpp
 *
 *  Created on: Nov 12, 2011
 *      Author: kimi
 */

#include "RSA.h"

RSA::RSA(unsigned long _key_length) {
	/*
	 private_keys_length = props->getInt("RSA_KEY_LENGTH");
	 if (private_keys_length > sizeof(unsigned long))
	 private_keys_length = sizeof(unsigned long);

	 private_keys_length /= 2;
	 private_keys_length = pow(2, private_keys_length) - 1;
	 */
//	cout << "Num of Bytes: " << _key_length << endl;
	key_length = (pow(2, _key_length*8) - 1)/2;
//	cout << "key length " << key_length << endl;
}

RSA::~RSA() {
	// TODO Auto-generated destructor stub
}

bool RSA::isPrime(unsigned long num) {
	unsigned long res = 0;
	unsigned long divs = 2;
	unsigned long mitad;

	if (num > 2) {
		mitad = floor((double) num / 2);
		do {
			res = (num % divs != 0);
			divs++;

		} while ((res != 0) && divs <= mitad);
	} else {
		res = 1;
	}

//	cout << "Primo: " << num << " - " << res << endl;
	return (res == 1?true:false);
}

unsigned long RSA::gcd(unsigned long a, unsigned long b) {
	if (b == 0) {
		return a;
	} else {
		return gcd(b, a % b);
	}
}

unsigned long long RSA::mcd(unsigned long long a, unsigned long long b) {
	a = a < 0 ? a * -1 : a;
	b = b < 0 ? b * -1 : b;
	a = a % b;
	if (a > 0)
		return mcd(b, a);
	else
		return b;
}

unsigned long long RSA::ext_mcd(unsigned long long a, unsigned long long b,
		unsigned long long r[2]) {
	unsigned long long x, y, k[2];

	if (a % b == 0) /* Si b divide a a entonces el maximo comun divisor es b */
	{
		r[0] = 0;
		r[1] = 1; /* La combinacion trivial es m = b*1 + a*0 donde m=b es el mcd ya que son multiplos */
		return 1;
	}
	ext_mcd(b, a % b, k);
	x = k[0];
	y = k[1];
	r[0] = y;
	r[1] = x - (y * (a / b));
	return 0;
}

unsigned long long RSA::mod_inv(unsigned long long a, unsigned long long n,
		unsigned long long* inv) {

	unsigned long long r[2];
	if (mcd(a, n) != 1) {
		*inv = -1;
		return -1;
	}
	ext_mcd(a, n, r);
	if (r[0] > 0) {
		*inv = r[0] % n;
		return r[0] % n;
	} else {
		*inv = (n - r[0]) % n;
		return (n - r[0]) % n;
	}
	*inv = -1;
	return -1;
}

/*
 * procedimiento que calcula el valor del maximo común divisor
 * de a y b siguiendo el algoritmo de euclides extendido,
 * devolviendo en un arreglo la siguiente estructura [d,x,y]
 * donde:
 *    mcd(a,b) = d = a*x + b*y
 */
unsigned long* RSA::euclides(unsigned long a, unsigned long b) {
	unsigned long* resp = new unsigned long[3];
	unsigned long x = 0, y = 0;

	if (b == 0) {
		resp[0] = a;
		resp[1] = 1;
		resp[2] = 0;
	} else {
		unsigned long x2 = 1, x1 = 0, y2 = 0, y1 = 1;
		unsigned long q = 0, r = 0;

		while (b > 0) {
			q = (a / b);
			r = a - q * b;
			x = x2 - q * x1;
			y = y2 - q * y1;
			a = b;
			b = r;
			x2 = x1;
			x1 = x;
			y2 = y1;
			y1 = y;
		}

		resp[0] = a;
		resp[1] = x2;
		resp[2] = y2;
	}

	return resp;
}

unsigned long long RSA::mod_exp(unsigned long long b, unsigned long long e,
		unsigned long long m) {
	unsigned long long r = 1;
	while (e > 0) {
		if ((e & 1) > 0)
			r = r * b % m;
		e >>= 1; /* mas rapido las potencias de dos son adquiridas asi, 1,10,100,1000 (binario) */
		b = (b * b) % m;
	}
	return r;
}

void RSA::generatePAndQ(unsigned long *p, unsigned long *q) {

	*p = 47;
	*q = 59;
	return;

	srand48((unsigned) time(0));
	unsigned long random_integer;
	random_integer = (rand() % key_length);

	while (1) {
		// buscamos un número primo, a partir de random_integer;
		if (isPrime(random_integer) == false) {
			random_integer++;
		} else {
			break;
		}
	}
	*p = random_integer;

	random_integer++;
	while (1) {
		// buscamos un número primo, a partir del ultimo primo
		if (isPrime(random_integer) == false) {
			random_integer++;
		} else {
			break;
		}
	}
	*q = random_integer;

//	cout << "P= " << *p << endl;
//	cout << "Q= " << *q << endl;
}

void RSA::generatePrivateAndPublicKey(unsigned long n, unsigned long phi,
		unsigned long *pk, unsigned long *pu) {
	unsigned long d = n / 2;
	while (gcd(d, phi) != 1 && isPrime(d) == false) {
		d--;
	}

	// ya tenemos d, cuyo unico común divisor con phi es 1
	unsigned long long e = 0;
	if (mod_inv((unsigned long long) d, (unsigned long long) phi, &e) == -1) {
		cerr << "No se ha encontrado el inverso multiplicativo para D= " << d
				<< " y Phi= " << phi << endl;
	}

	*pk = e;
	*pu = d;

//	cout << "PK= " << *pk << endl;
//	cout << "PU= " << *pu << endl;
}

void RSA::generateNAndPhi(unsigned long *n, unsigned long *phi) {
	*n = 0;
	*phi = 0;

	unsigned long p, q;

//	cout << "Generando P y Q" << endl;
	generatePAndQ(&p, &q);

	*n = p * q;
	(*phi) = ((unsigned long) (p - 1)) * ((unsigned long) (q - 1));

//	cout << "N= " << *n << endl;
//	cout << "PHI= " << *phi << endl;
}

unsigned long RSA::encrypt(unsigned long e, unsigned long n,
		unsigned long msg) {
	unsigned long encryptedMsg;

	encryptedMsg = mod_exp(msg, e, n);

	return encryptedMsg;
}

unsigned long RSA::decrypt(unsigned long d, unsigned long n,
		unsigned long encryptedMsg) {
	unsigned long decryptedMsg;

	decryptedMsg = mod_exp(encryptedMsg, d, n);

	return decryptedMsg;
}

string RSA::encrypt(unsigned long e, unsigned long n,
		string message) {
	stringstream ss;

	const char* msg = message.c_str();
	unsigned long msg_len = strlen(msg);

	char* buffer = new char[msg_len*3]; // cada char tiene 3 digitos como maximo
	unsigned long buffer_index = 0;

	unsigned long hi,mid,low;

	for (unsigned int i = 0; i < msg_len; i++) {
		/// separamos el mensaje en digitos
		char c = msg[i];
		hi = c/100;
		mid = (c - hi*100)/10;
		low = (c - mid*10 - hi*100);

		buffer[buffer_index++] = hi;
		buffer[buffer_index++] = mid;
		buffer[buffer_index++] = low;

//		cout << "char: " << c << " - " << hi << " "<< mid << " " << low <<endl;
	}

	bool last_case = false;
	unsigned long temp = 0;

	unsigned long digits_to_pack = log10(n);
//	cout << "Digits to Pack= " << digits_to_pack << endl;
	for (unsigned long i = 0; i < buffer_index; i++) {

		temp = 0;
		for (unsigned int j = 0; j < digits_to_pack && i < buffer_index; j++) {
			temp *=10;
			temp += buffer[i];
			i++;
		}
		i--;

		// encrypt temp
		unsigned long _temp_ = encrypt(e,n,temp);

//		cout << "E(" << temp << ")= " << _temp_ <<endl;

		// save temp
		unsigned int j = (log10(_temp_))+1;
		while (j <= digits_to_pack) {
			j++;
		//	cout << "+ 0" << endl;
			ss << "0";
		}

		ss << _temp_;

	}

//	cout << "encrypted= " << ss.str() << endl;


	return ss.str();
}

string RSA::decrypt(unsigned long d, unsigned long n,
		string message) {
	stringstream ss;
	string decrypted;

	//cout << endl << endl;

	const char* msg = message.c_str();
	unsigned long msg_len = strlen(msg);

	unsigned long digits_to_pack = log10(n) +1;
	//cout << "Digits to Pack= " << digits_to_pack << endl;

	unsigned long temp = 0;
	for (unsigned long i = 0; i <= msg_len; i++) {
/*
		while (temp < n && i < msg_len) {
			temp *= 10;
			temp += (msg[i]-'0');
	//		cout << "TEMP= " << temp << endl;
			i++;
		}
		while (temp > n) {
	//		cout << "temp>n " << temp << " i= " << i << endl;
			i--;
			temp -= (msg[i] -'0');
			temp /= 10;
		}

	//	cout << "A des. " <<temp << " i=" << i << endl;
*/
		temp = 0;
		for (unsigned int j = 0; j < digits_to_pack && i < msg_len; j++) {
			temp *=10;
			temp += (msg[i] -'0');
			i++;
		}
		i--;

		if(temp == 0)
			break;

		// encrypt temp
		unsigned long _temp_ = decrypt(d,n,temp);

//		cout << "D("<<temp<<")= " <<_temp_ << endl;

		// save temp
		ss << _temp_;

		// leave buffer[i] in temp
		temp = (msg[i] -'0');
	}

	decrypted = ss.str();

//	cout << "decrypted= " << decrypted << endl;

	ss.str(string());

	//body
	char c;
	temp = 0;
	unsigned long index = 0;

	for (; index < decrypted.length(); index++) {
		while (temp < 255 && index < decrypted.length()) {
			c = decrypted.at(index);
			temp += (c - '0');
			temp *= 10;
			index++;
		}
		if (temp > 255) {
			index--;
			temp /= 10;
		}

		//cout << "char= " << temp << endl;
		ss << ((char)(temp));
		temp = 0;
	}

	return ss.str();
}

bool RSA::encrypt(unsigned long e, unsigned long n, string sourceFile, string destFile) {
	ifstream plainFile;
	plainFile.open(sourceFile.c_str(),fstream::in);

	ofstream encryptedFile;
	encryptedFile.open(destFile.c_str(),fstream::out|fstream::trunc);

	if (!plainFile.is_open()) {
		cerr << "error archivo origen. "<< sourceFile <<endl;
		return false;
	}

	if 	(!encryptedFile.is_open()) {
		cerr << "error archivo destino " << destFile <<endl;
		return false;
	}

	string aLine;
	string bLine;
	while (getline(plainFile, aLine)) {
		bLine = encrypt(e,n,aLine);
		encryptedFile << bLine << endl;
	}

	encryptedFile.flush();
	encryptedFile.close();

	plainFile.close();

	return true;
}
bool RSA::decrypt(unsigned long d, unsigned long n, string sourceFile, string destFile) {
	ifstream encryptedFile;
	encryptedFile.open(sourceFile.c_str());

	ofstream plainFile;
	plainFile.open(destFile.c_str(),fstream::out|fstream::trunc);

	if (plainFile.is_open() == false || encryptedFile.is_open() == false) {
		return false;
	}

	string aLine;
	string bLine;
	while (getline(encryptedFile, aLine)) {
		bLine = decrypt(d,n,aLine);
		plainFile << bLine << endl;
	}

	plainFile.flush();
	plainFile.close();

	encryptedFile.close();

	return true;
}


bool RSA::encryptBinaryFile(unsigned long e, unsigned long n, string sourceFile, string destFile) {

	ifstream plainFile;
	plainFile.open(sourceFile.c_str(),fstream::in|fstream::binary);

	ofstream encryptedFile;
	encryptedFile.open(destFile.c_str(),fstream::out|fstream::trunc|fstream::binary);

	if (!plainFile.is_open()) {
		cerr << "error archivo origen. "<< sourceFile <<endl;
		return false;
	}

	if 	(!encryptedFile.is_open()) {
		cerr << "error archivo destino " << destFile <<endl;
		return false;
	}

	char plainChar;
	unsigned long encryptedChar;

	plainFile.seekg(0, ios::end);  // position get-ptr 0 bytes from end
	ios::pos_type size = plainFile.tellg();  // get-ptr position is now same as file size

	plainFile.seekg(0, ios::beg);

	char * toWrite = new char[sizeof(unsigned long)];
	for (unsigned long index = 0; index < size ; index++) {
		// leo un char
		plainFile.read(&plainChar,sizeof(char));

		unsigned char plainChar2 = (unsigned char)plainChar;

//		cout << "char leido: " << (long)plainChar2 << " ";

		// lo encripto
		encryptedChar = encrypt(e,n,(unsigned long)plainChar2);

//		cout << "char encrip: " << (unsigned long)encryptedChar << " ";

		// convierto a char*
		char* p= reinterpret_cast<char*>(&encryptedChar);
		for (unsigned int i = 0; i < sizeof(unsigned long); i++) {
			toWrite[i] = *p;
			p++;
		}
		// escribo en archivo
		encryptedFile.write(toWrite,sizeof(unsigned long));

		//cout << endl;
	}
	delete toWrite;

	encryptedFile.flush();
	encryptedFile.close();

	plainFile.close();

	return true;
}

bool RSA::decryptBinaryFile(unsigned long d, unsigned long n, string sourceFile, string destFile) {
	bool res = false;

//	cout << "decryptBinaryFile" << endl;

	ifstream encryptedFile;
	encryptedFile.open(sourceFile.c_str(),fstream::in|fstream::binary);

	ofstream plainFile;
	plainFile.open(destFile.c_str(),fstream::out|fstream::trunc|fstream::binary);

	if (!plainFile.is_open()) {
		cerr << "error archivo destino. "<< sourceFile <<endl;
		return false;
	}

	if 	(!encryptedFile.is_open()) {
		//cerr << "error archivo origen " << destFile <<endl;
		//Lo creamos!
		encryptedFile.open(sourceFile.c_str(),fstream::trunc|fstream::binary);
		res = true;
	}
	else {
//		cout << "process" << endl;

		char plainChar;
		unsigned long encryptedChar = 0;

		encryptedFile.seekg(0, ios::end);  // position get-ptr 0 bytes from end
		ios::pos_type size = encryptedFile.tellg();  // get-ptr position is now same as file size

		encryptedFile.seekg(0, ios::beg);

//		cout << "tamanio: " << (unsigned long)(size) << endl;
//		cout << "tamanio/4: " << (unsigned long)(size/sizeof(unsigned long)) << endl;

		unsigned long limit = (unsigned long)(size/sizeof(unsigned long));

		for (unsigned long index = 0; index < limit; index++) {
			char * toRead = new char[sizeof(unsigned long)];
			// leo un char
//			cout << "Index: " << index << endl;

			//cout << "leyendo de encriptado" << endl;

			encryptedFile.read(toRead,sizeof(unsigned long));

			// lo convierto a ulong
			char* p= reinterpret_cast<char*>(&encryptedChar);
			for (unsigned int i = 0; i < sizeof(unsigned long); i++) {
				*p = toRead[i];
				p++;
			}
			//cout << "convertido" << endl;

//			cout << "long leido: " << (unsigned long)encryptedChar << " ";

			// desencripto
			plainChar = decrypt(d,n,encryptedChar);

//			cout << "long desenc: " << (unsigned long)(plainChar&0xFF) << " " << endl;

			//cout << "escribiendo" << endl;
			// escribo en archivo

			plainFile.write(&plainChar,sizeof(char));
		//	cout << "wrote: "<<index << endl;
		}
		res = true;
	}
	plainFile.flush();
	plainFile.close();

	encryptedFile.close();
//	cout << "finished: " << res << endl;
	return res;
}



bool RSA::createPrivateKeyFile(string file, unsigned long d, unsigned long n) {
	Properties *privateKeyFile = new Properties(file);
	privateKeyFile->put("ALGORITHM","RSA");
	privateKeyFile->put("TYPE","Private Key");
	privateKeyFile->putInt("N",n);
	privateKeyFile->putInt("D",d);
	return privateKeyFile->save();
}
bool RSA::createPublicKeyFile(string file, unsigned long e, unsigned long n) {
	Properties *publicKeyFile = new Properties(file);
	publicKeyFile->put("ALGORITHM","RSA");
	publicKeyFile->put("TYPE","Public Key");
	publicKeyFile->putInt("N",n);
	publicKeyFile->putInt("E",e);
	return publicKeyFile->save();
}



